All files / packages/design-system-2-site/src/app/(frontend)/[category]/[...slug] page.tsx

95.23% Statements 40/42
90.69% Branches 39/43
100% Functions 5/5
100% Lines 37/37

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114                          1x                     1x 1x 1x   1x 5x 5x 5x 5x   5x 5x 5x 5x   5x     1x         15x 15x 15x       6x 6x 6x   5x 6x         6x 6x   6x       6x             11x 11x 11x   10x 10x       11x     11x 11x       11x       11x                              
import path from 'node:path';
import type { Metadata } from 'next';
import { notFound } from 'next/navigation';
import { MarkdownPage } from '@/components/MarkdownPage';
import { getAllContentFiles, getContentBySlug } from '@/lib/content';
import {
  getAdjacentPages,
  getBreadcrumbs,
  getCategoryBySlug,
  getPageByUrl,
  getTabContext,
} from '@/lib/navigation';
 
export const dynamicParams = false;
 
type RouteParams = { category: string; slug?: string[] };
 
/**
 * Static-export catch-all: emits one route per `content/**\/*.md(x)` file,
 * skipping content-root files (home.md, navigation.md) which are handled
 * elsewhere. Only paths returned here are built — `dynamicParams = false`
 * makes any unlisted request 404 at runtime.
 */
export async function generateStaticParams(): Promise<RouteParams[]> {
  const contentDir = path.resolve(process.cwd(), 'content');
  const params: RouteParams[] = [];
  const seen = new Set<string>();
 
  for (const filePath of getAllContentFiles(contentDir)) {
    const rel = path.relative(contentDir, filePath).replace(/\\/g, '/');
    const withoutExt = rel.replace(/\.mdx?$/, '');
    const segments = withoutExt.split('/');
    Iif (segments.length < 2) continue;
 
    const [category, ...rest] = segments;
    const key = `${category}/${rest.join('/')}`;
    Iif (seen.has(key)) continue;
    seen.add(key);
 
    params.push({ category, slug: rest });
  }
 
  return params;
}
 
/** The route's URL, the nav page it belongs to, and that page's tab strip (if any). */
function resolveNav(category: string, slug: string[] | undefined) {
  const url = `/${[category, ...(slug ?? [])].join('/')}`;
  const navPage = getPageByUrl(url);
  return { url, navPage, tabContext: navPage ? getTabContext(navPage, url) : null };
}
 
export async function generateMetadata({ params }: { params: Promise<RouteParams> }): Promise<Metadata> {
  const { category, slug } = await params;
  const content = await getContentBySlug(category, slug);
  if (!content) return {};
 
  const rawDescription = content.metadata.description ?? content.metadata.tagline;
  const description = typeof rawDescription === 'string' ? rawDescription : undefined;
 
  // A tab page is titled "<parent> - <tab>" so browser tabs and search results
  // distinguish "Badge - Usage" from "Badge - Accessibility". The parent's nav
  // name is used because tab front-matter titles already carry the tab name.
  const { tabContext } = resolveNav(category, slug);
  const activeTab = tabContext?.tabs.find((t) => t.active);
  const titleBase =
    activeTab && tabContext && activeTab.url !== tabContext.parent.url
      ? `${tabContext.parent.name} - ${activeTab.name}`
      : content.metadata.title;
 
  return {
    title: `${titleBase} | SLDS 2`,
    description,
  };
}
 
export default async function ContentPage({ params }: { params: Promise<RouteParams> }) {
  const { category, slug } = await params;
  const content = await getContentBySlug(category, slug);
  if (!content) notFound();
 
  const { url, navPage, tabContext } = resolveNav(category, slug);
  const breadcrumbs = navPage ? getBreadcrumbs(navPage) : [];
 
  // Tab pages share their parent's place in the nav, so the pager and landing
  // check both run against the parent page.
  const { prev, next } = navPage ? getAdjacentPages(navPage) : { prev: null, next: null };
 
  // A category's first child is its landing page (top level); everything deeper is second level.
  const navCategory = navPage ? getCategoryBySlug(navPage.category ?? category) : undefined;
  const isTopLevel = !!navPage && navCategory?.children[0]?.url === navPage.url;
 
  // The subtitle shows on the parent page only, not on its other tabs.
  const description =
    typeof content.metadata.description === 'string' && url === (navPage?.url ?? url.split('--')[0])
      ? content.metadata.description
      : undefined;
 
  return (
    <MarkdownPage
      // Tabs share the parent's heading (Zeroheight / POC); the tab strip names the tab.
      frontmatter={{ title: tabContext?.parent.name ?? content.metadata.title }}
      html={content.html}
      tabs={tabContext?.tabs}
      breadcrumbs={breadcrumbs}
      markdown={content.rawMarkdown}
      description={description}
      prev={prev && { name: prev.name, url: prev.url }}
      next={next && { name: next.name, url: next.url }}
      isTopLevel={isTopLevel}
    />
  );
}